
package com.feib.soeasy.job.importor;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.Assert;

import com.feib.soeasy.model.ColumnDefinition;
import com.feib.soeasy.model.ColumnValidateError;
import com.feib.soeasy.model.ExtraColumns;
import com.feib.soeasy.model.FormatDefinition;
import com.feib.soeasy.model.PayerTemp;
import com.feib.soeasy.model.Payment;
import com.feib.soeasy.model.PaymentItem;
import com.feib.soeasy.model.UploadFile;
import com.feib.soeasy.service.PayerTempService;
import com.feib.soeasy.job.service.ImportService;
import com.feib.soeasy.job.util.UnicodeInputStream;


/**
 * @title (#)FileChecker.java<br>
 * @description <br>
 * @author Jeff Gu<br>
 * @version 1.0.0 2010/11/25
 * @copyright Far Eastern International Bank Copyright (c) 2010<br>
 * @2010/11/23 create by Jimmy Liu<br>
 */
public class PayerTempFileChecker {

   
    
    /**
     * 檔案編碼
     */
    private String charsetName = "MS950"; // or NULL to use systemdefault

    /**
     * 檔案來源
     */
    private File sourceFile;

    private UploadFile uploadFile;

    private FormatDefinition formatDefinition;

    private BufferedReader reader; 

    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
    
    private List<ColumnValidateError> columnValidateErrors = new ArrayList<ColumnValidateError>(); // 錯誤紀錄
    
    private List<PayerTemp> payerTemps = new ArrayList<PayerTemp>(); //繳費人
    
    private boolean dontInsertPayerTemp = false; // 因為有檢核錯誤(columnValidateErrors.size > 0)，不需要新增繳費單到資料表中，若已新增的，則需要刪除
        
   
    
    private ImportService importService;
    
    private PayerTempService payerTempService ;
    
    private String fileDownloadDestination ;
    
    private PayerTempValidator payerTempValidator;
    

    public PayerTempFileChecker (UploadFile uploadFile, ImportService importService,PayerTempService payerTempService  ,String fileDownloadDestination) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException
    {
        Assert.notNull(uploadFile, "uploadFile 不可以為NULL");

        this.uploadFile = uploadFile;
        this.sourceFile = new File(uploadFile.getFilePathOnServer());
        // 取得轉入格式定義檔
        this.formatDefinition = uploadFile.getFormatDefinition();
        Assert.notNull(formatDefinition, "formatDefinition 不可以為NULL");
        this.importService = importService;
        Assert.notNull(importService, "importService 不可以為NULL");
        this.payerTempService = payerTempService;
        Assert.notNull(payerTempService, "payerTempService 不可以為NULL");
        
        
        
        // 客製化檢查
        if (null != formatDefinition.getCustomValidateClass()){
            payerTempValidator = (PayerTempValidator)Class.forName(formatDefinition.getCustomValidateClass())
                .getConstructor(UploadFile.class, FormatDefinition.class, List.class,ImportService.class, PayerTempService.class)
                .newInstance(uploadFile, formatDefinition, columnValidateErrors,importService,payerTempService);
        }
        else
        {
            payerTempValidator = new PayerTempValidator(uploadFile, formatDefinition, columnValidateErrors, importService, payerTempService);
        }
        
        Assert.notNull(fileDownloadDestination, "fileDownloadDestination 不可以為NULL");
        
        this.fileDownloadDestination = fileDownloadDestination;
    }

    public boolean checkFile() throws UnsupportedEncodingException, IOException
    {
        try {
            
            //================================================================//
            try {
                FileInputStream fis = new FileInputStream(this.sourceFile);
                UnicodeInputStream uin = new UnicodeInputStream(fis, this.charsetName);
                charsetName = uin.getEncoding(); // check and skip possible BOM bytes

                InputStreamReader isr;
                if (null == this.charsetName){
                    isr = new InputStreamReader(uin);
                }
                else {
                    isr = new InputStreamReader(uin, this.charsetName);
                }

                reader = new BufferedReader(isr);
            }
            catch (FileNotFoundException e) {
                throw new RuntimeException("檔案位置錯誤("+sourceFile.getAbsolutePath()+")", e);
            }
            catch (UnsupportedEncodingException e) {
                throw new RuntimeException("系統不支援此編碼:" + this.charsetName, e);
            }

            PyerImportJob.logger.debug("使用編碼{}拆解字串 ", this.charsetName);
            //================================================================//
            
            List<ColumnDefinition> columnDefinitions = formatDefinition.getColumnDefinitions();
            int lineLength = 0;
            // 取 EndIndex 最大的為 每一行的總長度
            for(ColumnDefinition columnDefinition : columnDefinitions)
            {
                if (null != columnDefinition.getEndIndex())
                    lineLength = Math.max(lineLength, columnDefinition.getEndIndex().intValue());
            }
            PyerImportJob.logger.debug("字串長度應為 {} bytes ", lineLength);

            Charset charset = Charset.forName(charsetName);

            String lineData;
            byte[] lineBytes;
            long lineNumber = 0; // 行號
           
            // 逐行檢查
            while((lineData = reader.readLine()) != null) {
                try {

                    lineNumber ++ ;

                    lineBytes = lineData.getBytes(charsetName);

                    // step1 : 檢查長度，若長度不對，不用再往下檢查
                    if (lineLength != lineBytes.length )
                    {
                        // 長度不不符
                        addColumnValidateError(lineNumber
                                        , null
                                        , "長度不符，應為["+lineLength+"]，但實際為["+lineBytes.length+"]", null);
                        
                        // 此筆資料不需再做其他檢查，跳到下一筆資料
                        continue;
                    }


                    // step2 : 建立一筆新的繳費人，若檢核都通過，才可以新增到資料庫中
                    PayerTemp payerTemp = new PayerTemp();
                   
                    boolean isConvertError = false; // 資料轉型是否有錯誤
                    boolean isBindingError = false; // 資料繫結至payerTemp物件是否有錯誤
                    boolean isValidateError = false;
                    
                    for(ColumnDefinition columnDefinition : columnDefinitions)
                    {
                        int beginIndex = 0; // 起始位置
                        int endIndex = 0; // 結束位置
                        isConvertError = false; // 資料轉型是否有錯誤
                        isBindingError = false; // 資料繫結至Payertemp物件是否有錯誤
                        
                        // step3 : 將檔案上的字串轉成所設定的型態
                        String value = "";
                        Object convertedValue = null; //轉行後的值, ex: 20101112->Date 物件
                        if (null == columnDefinition.getBeginIndex())
                        {
                            // 未定義
                            // do nothing
                            PyerImportJob.logger.debug("columnDefinition.getBeginIndex() ", columnDefinition.getBeginIndex());

                        }
                        else
                        {
                            beginIndex = columnDefinition.getBeginIndex().intValue();
                            endIndex = columnDefinition.getEndIndex().intValue();
                            // 轉成 UTF-8字串
                            value = new String(new String(lineBytes, beginIndex-1, endIndex- beginIndex+1, charset).getBytes("UTF-8"),"UTF-8");
                            // 去空白
                            value = value.trim();
                            
                            //顯示數據
                            PyerImportJob.logger.debug("value:{} ",value);

                            
                            
                        }


                        // Data Converting...
                        if(null != columnDefinition.getRequired() && columnDefinition.getRequired().booleanValue())
                        {
                            if (0 == value.length())
                            {
                                // 設定為必須輸入欄位，但是卻是空字串
                                addColumnValidateError(lineNumber
                                                , columnDefinition.getColumnTitle() + "("+beginIndex + "-" + endIndex +")"
                                                , "必須輸入欄位", null);
                                
                                //顯示必須輸入欄位
                                PyerImportJob.logger.debug("必須輸入欄位 ",lineNumber);
                                
                                // 此筆資料不需再做其他檢查，跳到下一筆資料
                                continue;
                            }                        
                        }

                        if ("1".equals(columnDefinition.getColumnType()))
                        {
                            // string                        
                            convertedValue = ((0 == value.length()) ? null : value);
                            //顯示轉換後數值
                            PyerImportJob.logger.debug("convertedValue:{}",convertedValue);
                        }
                        else if ("2".equals(columnDefinition.getColumnType()))
                        {
                            // number
                            try {
                                if (null != value && 0 != value.length())
                                {
                                    if (value.startsWith("+") || value.startsWith("-"))
                                    {
                                        boolean positive = value.startsWith("+");
                                        
                                        String tempValue = value.substring(1);
                                                                                
                                        convertedValue = Double.valueOf(tempValue);
                                        
                                        if (!positive)
                                        {
                                            convertedValue = new Double(0 - ((Double)convertedValue));
                                        }
                                    }
                                    else
                                        convertedValue = Double.valueOf(value);
                                    
                                    // add by Jimmy 20110413 for 轉型錯誤
                                    Class<Object> classType = PropertyUtils.getPropertyType(payerTemp, columnDefinition.getColumnName());
                                    if (classType.equals(Long.class))
                                    {
                                        convertedValue = new Long(((Double)convertedValue).longValue());
                                    }
                                    else if (classType.equals(Integer.class))
                                    {
                                        convertedValue = new Integer(((Double)convertedValue).intValue());
                                    }
                                    
                                }
                               
                            }
                            catch (Exception e) {
                                // 無法轉成數字
                                isConvertError = true;
                                addColumnValidateError(lineNumber
                                                , columnDefinition.getColumnTitle() + "("+beginIndex + "-" + endIndex +")"
                                                , "[" + (StringUtils.isEmpty(value) ? "空白" : value) + "] 不是數字格式", null);
                            }
                        }
                        else if ("3".equals(columnDefinition.getColumnType()))
                        {
                            // date
                            try {
                                if (null != value && 0 != value.length())
                                    convertedValue = dateFormat.parse(value);
                            }
                            catch (Exception e) {
                                // 無法轉成日期
                                isConvertError = true;
                                addColumnValidateError(lineNumber
                                                , columnDefinition.getColumnTitle() + "("+beginIndex + "-" + endIndex +")"
                                                , "[" +(StringUtils.isEmpty(value) ? "空白" : value) + "] 不是日期格式("+ this.dateFormat.toPattern()+")", null);
                            }
                        }
                        else
                        {
                            throw new RuntimeException("欄位設定錯誤，應該只能是 1:String, 2:Number, 3:Date 其中之一");
                        }
                        
                        // step4 : 將值放入對應的物件欄位中
                        // 表頭  或  明細欄位//繳款人沒有明細
                        boolean isHeader = (null != columnDefinition.getHeader() && columnDefinition.getHeader().booleanValue());
                        String columnName = columnDefinition.getColumnName();
                        if (isHeader)
                        {
                            try {
                                    //顯示值注入物件
                                    PyerImportJob.logger.debug("值注入物件　行  {}",lineNumber);

                                    PropertyUtils.setProperty(payerTemp, columnName, convertedValue);
                              
                            }
                            catch (Exception e) {
                                isBindingError = true;
                                addColumnValidateError(lineNumber
                                        , columnDefinition.getColumnTitle() + "("+beginIndex + "-" + endIndex +")"
                                        , "型態錯誤，請檢查設定檔是否有錯！"
                                        , e);
                                
                                PyerImportJob.logger.debug("值注入物件時發生錯誤: 型態錯誤   {}",e);
                              //  throw new RuntimeException("值注入物件時發生錯誤: 型態錯誤 {}", e);
                                
                                
                            }
                        }
                    } //for(ColumnDefinition columnDefinition : columnDefinitions)
                                        
                    if (isConvertError || isBindingError)
                    {
                        // 轉型物資料繫結有發生錯誤
                        // 此筆資料不需再做其他檢查，跳到下一筆資料
                        
                        PyerImportJob.logger.debug("轉型物資料   繫結有發生錯誤  {} {}",isConvertError,isBindingError);

                        continue;
                        
                        
                    }
                    
                    // step5 : 一般基本檢核 與 帳單業者特殊檢核
                    //先設定 group 滿足   測試中使用  PayerTempService查詢 
                    String grono=uploadFile.getGroup().getGroupNo();
                    
                    PyerImportJob.logger.debug("uploadFile.getGroup {} ", grono);
                    
                    payerTemp.setGroup(uploadFile.getGroup());
                    
                    
                    String pgn=   payerTemp.getGroup().getGroupNo(); 
                    String pn=  payerTemp.getPayerNo();
                                      
                    PyerImportJob.logger.debug("payerTemp getGroupNo {}", pgn);
                    
                    PyerImportJob.logger.debug("payerTemp getPayerNo  {}" , pn);
                    
                    isValidateError = ! this.payerTempValidator.payerTempValidate(payerTemp,lineNumber);
                    
                    if (!isValidateError) 
                    {
                        // step6 : 檢核通過，將資料新增到資料庫中
                        addPayerTemp(payerTemp);
                    }
                    
                    // 檢核錯誤就設定不需要在新增了
                    if (isValidateError)
                    {
                        PyerImportJob.logger.debug("isValidateError{} ", isValidateError);
                        dontInsertPayerTemp = true;
                    }
                    
                    // free resource
                   
                }
                catch (Exception e) {
                    
                    PyerImportJob.logger.error("逐行檢查錯誤:", e);
                    addColumnValidateError(lineNumber, null, "系統錯誤！", e);
                    
                    throw new RuntimeException(e);
                }
                finally
                {
                    // do nothing.
                }

            } //while((lineData = reader.readLine()) != null)
            
            // 總筆數
            
            uploadFile.setTotalItems(lineNumber);
      
            
            importService.updateUploadFileTotals(uploadFile);
        }
        /*
        catch(Exception e)
        {
            
            passed = false;
            dontInsertPayerTemp = true;
            PyerImportJob.logger.error("檢核過程發生錯誤:", e);
            addColumnValidateError(0, null, "系統錯誤！", e);
            
        }
        */
        finally
        {            
            try {
                if (null != reader)
                    reader.close();
            }
            catch (IOException e) {
                // do nothing
            }
            reader = null;
            

            if (dontInsertPayerTemp)
            {
                // 確保帳單都刪除  待改
               payerTempService.deletePayerTempsByUploadFile(uploadFile);
                
                // 將錯誤資料寫到資料表中
                flushColumnValidateErrors();
            }
            else
            {
                flushPayerTemps();
            }           
        }
        
        return !dontInsertPayerTemp;
    }
    
    
    /**
     * 從資料庫中刪除繳費人單
     */
    protected void deletePayerTemp(PayerTemp payerTemp) {
        payerTempService.delete(payerTemp);
    }

    /**
     * 新增繳費人單
     * @param payment
     */
    protected void addPayerTemp(PayerTemp payerTemp)
    {
        if (!dontInsertPayerTemp)
        {
            
            payerTemp.setGroup(this.uploadFile.getGroup());
            payerTemp.setPayerStatus(null);
            payerTemp.setUploadFile(uploadFile);
            
            this.payerTemps.add(payerTemp);
            
            //顯示 payerTemps中內容
            for(PayerTemp p:payerTemps){
             
                
                PyerImportJob.logger.debug("payerTemp getPayerName {}", p.getPayerName());
                PyerImportJob.logger.debug("payerTemp getGroup.getGroupNo() {}", p.getGroup().getGroupNo() );
                PyerImportJob.logger.debug("payerTemp p.getUploadFile() {}", p.getUploadFile() );
              PyerImportJob.logger.debug("payerTemp p.getUploadFile() {}", p.getUploadFile().getUploadFilePk() );
            }
            
           
            
            
            if (payerTemps.size() >= 50)
            {
                // 將繳費資料新增到資料庫中
                flushPayerTemps();
                // 清除memory資料
                payerTemps.clear();
            }
        }
    }

    
    /**
     * 
     */
    protected void flushPayerTemps() {
        
        this.payerTempService.addPayerTemp(this.payerTemps);
        
    }

    /**
     * 加入一筆檢核錯誤原因，若累積超過50筆資料，則整批 新增到資料庫中
     * @param lineNumber 第幾行錯誤
     * @param columnName 錯誤欄位
     * @param errorMessage 錯誤原因
     * @param exceptionMessage 程式錯誤原因
     */
    protected void addColumnValidateError(long lineNumber, String columnName, String errorMessage, Exception exceptionMessage)
    {
        dontInsertPayerTemp = true; // 不需要再新增繳費單，並且需要刪除已新增進去的資料(程式結束時刪即可)
        
        ColumnValidateError error = new ColumnValidateError();
        error.setUploadFile(uploadFile);
        error.setLineNumber(lineNumber);
        error.setColumnName(columnName);
        error.setErrorMessage(errorMessage);
        if (null != exceptionMessage)
            error.setExceptionMessage(exceptionMessage.getMessage());
        
        PyerImportJob.logger.debug(error.toString());
        columnValidateErrors.add(error);
        
        if (columnValidateErrors.size() >= 50)
        {
            // 將錯誤資料新增到資料庫中
            flushColumnValidateErrors();
            // 清除memory資料
            columnValidateErrors.clear();
        }
    }
    
    
    protected void flushColumnValidateErrors()
    {
        this.importService.addColumnValidateErrors(this.columnValidateErrors);
    }

}
